Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transport: Bug fix in rename method #6735

Merged
merged 17 commits into from
Feb 10, 2025

Conversation

ayushjariyal
Copy link
Contributor

Fix #6725

Updated the condition to raise an appropriate error message (Destination already exists) when the path exists, improving code readability and correctness.

Copy link
Contributor

@khsrali khsrali left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ayushjariyal for your contribution, please apply the review.

if not self.isdir(newpath):
raise OSError(f'Destination {newpath} does not exist')

if self.isfile(newpath) or self.isdir(newpath):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if self.isfile(newpath) or self.isdir(newpath):
if self.path_exists(newpath):
raise OSError(f'Destination {newpath} already exists')

maybe this is easier, and perhaps more readable.
Also I'm not entirely sure, if existing symlinks would get caught by self.isfile

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also get rid of the comments above, line 1374-1377

@ayushjariyal
Copy link
Contributor Author

@khsrali , I have made the changes as per your instructions. Could you please review them?

@khsrali
Copy link
Contributor

khsrali commented Jan 29, 2025

@khsrali , I have made the changes as per your instructions. Could you please review them?

Please check here https://github.com/aiidateam/aiida-core/pull/6735/files
your files have merge conflicts. please fix that first.

@ayushjariyal
Copy link
Contributor Author

@khsrali I solved the merge conflict. Can you please check it?

@khsrali
Copy link
Contributor

khsrali commented Jan 29, 2025

bro, apply the review carefully 🥲
#6735 (comment)

Copy link
Collaborator

@danielhollas danielhollas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am quite confused by this bug. Does it mean that the function never worked at all?
Is it used anywhere else in the codebase? Would be good to have a test if possible.

raise OSError(f'Destination {newpath} does not exist')

if self.path_exists(newpath):
raise OSError(f'Destination {newpath} already exist')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring on line 1360 needs fixing. (github does not allow me to comment on that line)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the docstring on line 1360, it states that OSError is used for both cases—when oldpath and newpath both are not found—which needs to be fixed. However, is it okay to use specific errors (FileNotFoundError and FileExistsError) instead of OSError?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

                   :raises OSError: if oldpath is not found

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general. you are right FileNotFoundError might be a better fit for such cases, as with OSError is not immediately clear what's the error case.

However, please note OSError is the parent of FileNotFoundError

 └── Exception
      └── OSError
           ├── FileNotFoundError
           ├── PermissionError
           ├── IsADirectoryError
           ├── NotADirectoryError

For the sake of consistency --I assume-- OSError was chose to raise from our Transport class with proper message. This way it's easier to handle, when adopting any of the Transport plugins.

@khsrali
Copy link
Contributor

khsrali commented Jan 30, 2025

I am quite confused by this bug. Does it mean that the function never worked at all? Is it used anywhere else in the codebase? Would be good to have a test if possible.

yes, apparently never used across the interface 🥲

Agreed, adding tests is always a good idea! @ayushjariyal can you please write tests for this function? you can look for file test_all_plugins.py get inspiration from there and add your test with name def test_rename in it.
It's important before pushing you make sure it works, using pytest test_all_plugins.py::test_rename -s

@ayushjariyal
Copy link
Contributor Author

@khsrali I implemented the following test_rename function

Screenshot from 2025-02-06 11-21-51

and it produce this result

Screenshot from 2025-02-06 11-18-55

Is it correct, or does it need any improvements? Can I proceed with pushing this function?

@danielhollas danielhollas self-requested a review February 6, 2025 12:36
@ayushjariyal
Copy link
Contributor Author

@danielhollas Previously, some test-generated files and directories were not properly deleted, causing warnings during pytest cleanup. I have now removed these leftover files, ensuring that no garbage files interfere with the test execution. The tests should now complete without OSError: Directory not empty warnings.

Screenshot from 2025-02-06 19-14-03

@khsrali
Copy link
Contributor

khsrali commented Feb 6, 2025

@danielhollas Previously, some test-generated files and directories were not properly deleted, causing warnings during pytest cleanup. I have now removed these leftover files, ensuring that no garbage files interfere with the test execution. The tests should now complete without OSError: Directory not empty warnings.

@ayushjariyal thanks a lot! please push your changes so we can review it.
Don't worry about the garbage files, it's not related to your changes. That issue existed before.

@danielhollas
Copy link
Collaborator

Is it correct, or does it need any improvements? Can I proceed with pushing this function?

Looks reasonable. Always feel free to push, it's easier to discuss the code in GitHub UI once it is pushed. :-)

At the end of a test, if you expect that a call to a function should raise an exception, you should wrap it in pytest.raises(OSError) context manager, see https://docs.pytest.org/en/latest/how-to/assert.html#assertions-about-expected-exceptions

I think right now your test would pass even if the exception was not raised.

@khsrali
Copy link
Contributor

khsrali commented Feb 6, 2025

@ayushjariyal can you also open an issue, documenting the problem with garbage files? Cheers!
So probably we could solve that later.

@ayushjariyal
Copy link
Contributor Author

I encountered this issue while committing my code. Could you please clarify whether this error is due to something on my end or if it is system-dependent? I'm having trouble identifying the root cause.

Screenshot from 2025-02-07 19-18-26

@khsrali
Copy link
Contributor

khsrali commented Feb 7, 2025

Doesn't look like issues imposed by your changes. It's more likely that mypy version you have installed doesn't match.
But don't worry, just commit & push. If you get block by pre-commit you can do pre-commit uninstall to remove the hooks.

Alternatively, before you commit, you can run git add . & pre-commit run.
And just check the issues from your changes, only. This is usually the way, I do it.

Copy link
Contributor

@khsrali khsrali left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @ayushjariyal for the nice work! please see and apply the review.

Comment on lines 880 to 886
# if not os.path.exists(newpath):
# raise OSError(f'Destination {newpath} does not exist')

# Ensure the destination folder exists
dest_dir = os.path.dirname(newpath)
if dest_dir and not os.path.exists(dest_dir):
raise OSError(f'Destination directory {dest_dir} does not exist')
Copy link
Contributor

@khsrali khsrali Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing this! to me it's more convenient that you do the same check as you do for ssh.py.
I see that you suggest to check for directory, but in principle that should not be an issue, as this method should only be used for renaming and not moving files.

Suggested change
# if not os.path.exists(newpath):
# raise OSError(f'Destination {newpath} does not exist')
# Ensure the destination folder exists
dest_dir = os.path.dirname(newpath)
if dest_dir and not os.path.exists(dest_dir):
raise OSError(f'Destination directory {dest_dir} does not exist')
if os.path.exists(newpath):
raise OSError(f'Destination {newpath} already exists.')

also please update the docstring:

        :raises OSError: if oldpath is not found or newpath already exists.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ayushjariyal , maybe you forgot this? :)

@@ -1357,7 +1357,7 @@ def rename(self, oldpath: TransportPath, newpath: TransportPath):
:param str oldpath: existing name of the file or folder
:param str newpath: new name for the file or folder
:raises OSError: if oldpath/newpath is not found
:raises OSError: if oldpath is not found
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:raises OSError: if oldpath is not found
:raises OSError: if oldpath is not found or newpath already exists

Comment on lines 1240 to 1241
if not transport.is_open:
transport.open()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not transport.is_open:
transport.open()

You don't need this, because the context manager already should take care of that. See here and here

Comment on lines 1266 to 1269
# Cleanup files manually
for file in tmp_path_remote.iterdir():
file.unlink()
tmp_path_remote.rmdir() # Remove the directory if empty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good that you think of clean up. But since you are using tmp_path_remote, it's not needed really. pytest should empty the directory automatically after the test returns.

Suggested change
# Cleanup files manually
for file in tmp_path_remote.iterdir():
file.unlink()
tmp_path_remote.rmdir() # Remove the directory if empty

Comment on lines 1260 to 1264
try:
transport.rename(new_file, another_file)
print('Rename to existing file succeeded (no error).')
except OSError as e:
print(f'Rename failed as expected: {e}')
Copy link
Contributor

@khsrali khsrali Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest has a nice error capturing context manager, that can also match for an exact error message:

Suggested change
try:
transport.rename(new_file, another_file)
print('Rename to existing file succeeded (no error).')
except OSError as e:
print(f'Rename failed as expected: {e}')
with pytest.raises(OSError, match = "already exist"):
transport.rename(new_file, another_file)

@ayushjariyal ayushjariyal requested a review from khsrali February 9, 2025 07:05
@ayushjariyal ayushjariyal force-pushed the issue_6725_rename_function_bug branch from 5ff694e to 35372ae Compare February 9, 2025 18:14
Copy link

codecov bot commented Feb 9, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 78.14%. Comparing base (8c5c709) to head (383defc).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6735      +/-   ##
==========================================
+ Coverage   78.08%   78.14%   +0.06%     
==========================================
  Files         564      564              
  Lines       42547    42546       -1     
==========================================
+ Hits        33219    33242      +23     
+ Misses       9328     9304      -24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@khsrali khsrali left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ayushjariyal , Thanks again for your contribution.
LGTM!

@khsrali khsrali changed the title Solving bug in rename function Transport: Bug fix in rename method Feb 10, 2025
@khsrali khsrali merged commit f56fcc3 into aiidateam:main Feb 10, 2025
10 checks passed
@ayushjariyal
Copy link
Contributor Author

Thank you, @khsrali ! I really appreciate your time and review. Glad to contribute! Looking forward to more collaborations.

By the way, I was wondering if the organization plans to participate in GSoC 2025. I'd love to explore opportunities to contribute further.

@khsrali
Copy link
Contributor

khsrali commented Feb 10, 2025

Thanks again :)

yes, you can check here https://github.com/aiidateam/aiida-core/wiki/GSoC-2025-Projects
We put some ideas there, but we don't have a slot yet. And the project is not approved, yet.

@ayushjariyal
Copy link
Contributor Author

Thank you for sharing this, I really appreciate the information. I’ll check out the ideas and keep an eye on the updates. Looking forward to contributing more! 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Transport: SshTransport bug in method rename
3 participants